<template>
  <page-view>
    <template v-slot:header>
      <div class="header">
        <el-button type="primary" @click="drawRect">绘制矩形</el-button>
        <el-button type="primary" @click="drawPath">绘制路径</el-button>
        <el-button type="primary" @click="drawArc">绘制圆弧</el-button>
        <el-button type="primary" @click="drawDashLine">绘制虚线</el-button>
        <el-button type="primary" @click="drawText">绘制文本</el-button>
        <el-button type="primary" @click="drawImg">绘制图片</el-button>
        <el-button type="success" @click="save">保存整个canvas绘制</el-button>
        <el-button type="success" @click="saveTargetRect">保存canvas指定区域绘制</el-button>
      </div>
    </template>
    <el-row>
      <el-col :span="10">
        <div class="content-left">
          <div>原始图片</div>
          <img class="target-image" id="myImage" width="640" height="360" :src="handelImage" alt="">
          <div v-if="resultImage && resultImage.length > 0">canvas生成的图片</div>
          <img v-if="resultImage && resultImage.length > 0" :src="resultImage" alt="">
        </div>
      </el-col>
      <el-col :span="14">
        <canvas id="myCanvas" class="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
      </el-col>
    </el-row>
  </page-view>
</template>

<script>

// 学习地址：https://www.runoob.com/w3cnote/html5-canvas-intro.html

import targetImage from '@/assets/test.jpeg'
export default {
  name: "work-canvas",
  components: {},
  data() {
    return {
      // 画布宽高
      canvasWidth:900,
      canvasHeight:600,

      handelImage:targetImage,
      resultImage:'',
    }
  },
  mounted() {
    this.drawRect();
  },

  methods:{
    /**
     * 绘制矩形
     */
    drawRect() {
      this.clearCanvas();

      let canvas = document.getElementById('myCanvas');

      // 检测是否支持上下文（也就是画笔）
      if(!canvas.getContext) return;

      // 获得 2d 上下文对象
      let ctx = canvas.getContext("2d");

      // 绘制一个矩形 默认填充整个矩形背景色为黑色
      ctx.fillRect(10, 10, 100, 50);

      // 绘制一个矩形边框
      ctx.strokeRect(10, 70, 100, 50);

      // 清除指定的矩形区域，然后这块区域会变的完全透明。
      ctx.clearRect(15, 15, 50, 25);
    },

    /**
     * 绘制路径 所有图形的绘制都要通过绘制路径实现
     */
    drawPath() {
      this.clearCanvas();

      let canvas = document.getElementById('myCanvas');
      if (!canvas.getContext) return;
      let ctx = canvas.getContext('2d');

      // 填充样式（封闭内区域）(会更改全局的上下文样式)
      ctx.fillStyle ='#ffffff';
      // 直线的宽度
      ctx.lineWidth = 5;
      // 边框样式（矩形的边框）
      ctx.strokeStyle = '#FF4A20';

      // 新建一条path
      ctx.beginPath();

      // 把画笔移动到指定的坐标
      ctx.moveTo(50, 50);

      // 绘制一条从当前位置到指定坐标(200, 50)的直线.
      ctx.lineTo(200, 50);

      // 此时画笔关闭 就不会从(200,50)到(200,200) 如果不关闭 不仅从起点会向该点画一条 还会从中间点往下面一点继续画一条
      // ctx.closePath();

      ctx.lineTo(200, 200);

      // 闭合路径。会拉一条从当前点到path起始点的直线。如果当前点与起始点重合，则什么都不做（一旦调用则会形成一个闭合区域 否则不闭合）
      ctx.closePath();

      // 描边 绘制路径。stroke不会自动closePath()
      ctx.stroke();

      // 填充闭合区域。如果path没有闭合，则fill()会自动闭合路径。（形成一个闭合区域）
      ctx.fill();

      this.drawPath2();
    },

    /**
     * 绘制路径2 图形不闭合
     */
    drawPath2() {
      let ctx = this.getCurrentCtx();
      if (!ctx) return;
      ctx.beginPath();
      ctx.moveTo(300, 50);
      ctx.lineTo(400,50);
      ctx.lineTo(400,150);
      // 此时只描边 并没有对路径进行闭合 所有非闭合区域
      ctx.stroke();
    },

    /**
     * 清除整个画布区域内容
     */
    clearCanvas() {
      let canvas = document.getElementById('myCanvas');
      if (!canvas.getContext) return;
      let ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
    },

    /**
     * 绘制圆弧 1.里的度数都是弧度。2.0弧度是指的 x 轴正方向。
     * arc(x, y, r, startAngle, endAngle, anticlockwise): 以(x, y) 为圆心，以r 为半径，
     * 从 startAngle 弧度开始到endAngle弧度结束。anticlosewise 是布尔值，true 表示逆时针，false 表示顺时针(默认是顺时针)。
     */
    drawArc() {
      this.clearCanvas();
      let ctx = this.getCurrentCtx();
      if (!ctx) return;
      ctx.beginPath();
      ctx.arc(50, 50, 50, 0, Math.PI / 2, false);
      ctx.stroke();
      // 调用fill会闭合区域 自动调用closePath()
      ctx.fill();
    },

    /**
     * 绘制虚线
     * 用 setLineDash 方法和 lineDashOffset 属性来制定虚线样式。
     * setLineDash 方法接受一个数组，来指定线段与间隙的交替；lineDashOffset属性设置起始偏移量。
     */
    drawDashLine() {
      this.clearCanvas();
      let ctx = this.getCurrentCtx();
      if (!ctx) return;

      ctx.strokeStyle = '#FF9720';

      // [实线长度, 间隙长度]
      ctx.setLineDash([20, 5]);
      // 起始偏移量(决定其实位置样式 其实的线条宽度是两条边都会占还是直接单边)
      ctx.lineDashOffset = -0;
      ctx.strokeRect(50, 50, 210, 210);
    },

    /**
     * 绘制文本
     * 1.fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本，绘制的最大宽度是可选的。
     * 2.strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框，绘制的最大宽度是可选的
     */
    drawText() {
      this.clearCanvas();
      let ctx = this.getCurrentCtx();
      if (!ctx) return;
      ctx.font = "100px sans-serif";
      ctx.fillText("天若有情", 10, 100);
      ctx.strokeText("天若有情", 10, 200);
    },

    /**
     * 对图片源放到canvas里进行绘制 得到自己想要的图片
     */
    drawImg() {
      let ctx = this.getCurrentCtx();
      if (!ctx) return;
      let img = document.getElementById('myImage');
      console.log('img:', img);

      // 对指定图片 于canvas目标位置 缩放指定宽高
      ctx.drawImage(img, 0, 0, 400, 400);

      // 切片处理： 对指定图片进行切片处理 后面8个参数意思:前4个是定义图像源的切片位置和大小，后4个则是定义切片的目标显示位置和大小。
      ctx.drawImage(img, 128, 0, 150, 150, 250, 250, 150, 150);

      // 计算文本的宽度和高度
      let textFont = 14;
      ctx.fillStyle = '#FF4A20';
      ctx.font = `${textFont}px sans-serif`;
      let text = '2022-10-20 09:21:50';
      let name = '吴俊秋';
      let textWidth = ctx.measureText(text).width;
      let textHeight = textFont;

      let nameW = ctx.measureText(name).width;

      // 目标宽高内
      let specifiedWidth = 150;
      let specifiedHeight = 150;

      let contentTotalH = textHeight * 2 + 10;

      // 计算文本的中心点坐标
      let targetX = (specifiedWidth - textWidth) * 0.5;
      // 这里文本有个基线对齐方法
      let targetY = (specifiedHeight - contentTotalH) * 0.5 + textHeight;

      let timeY = 250 + targetY;
      ctx.fillText(text, 250 + targetX, timeY);

      let nameY = timeY + 10 + textHeight;
      targetX = (specifiedWidth - nameW) * 0.5;
      ctx.fillText(name, 250 + targetX, nameY);
    },

    /**
     * 获取当前画笔上下文对象
     */
    getCurrentCtx() {
      let canvas = document.getElementById('myCanvas');
      if (!canvas.getContext) return;
      let ctx = canvas.getContext('2d');
      return ctx;
    },

    /**
     * 保存canvas指定区绘制的内容 这样防止canvas过大 导致很多空白区域出现
     */
    saveTargetRect() {
      // 源绘制内容的canvas
      let originCanvas = document.getElementById('myCanvas');

      // 绘制的内容区
      let maxWidth = 400, maxHeight = 400;

      // 创建一个新的canvas 用以只导出绘制内容区 而不是整个canvas,有空白区域不是我想要的
      let sourceCanvas = document.createElement('canvas');
      sourceCanvas.width = maxWidth;
      sourceCanvas.height = maxHeight;
      let sourceCtx = sourceCanvas.getContext('2d');

      // 对源canvas做切片处理
      sourceCtx.drawImage(originCanvas, 0, 0,  maxWidth,  maxHeight, 0, 0, maxWidth, maxHeight);
      let imageUrl = sourceCanvas.toDataURL();
      console.log('保存canvas指定区域绘制的内容:', imageUrl);
      this.resultImage = imageUrl;
      sourceCanvas.remove()
    },

    /**
     * 保存canvas绘制内容(这里是整个canvas区域的绘制内容都会导出)
     */
    save() {
      // 注意:canvas绘制内容导出时 若存在非同源的图片 则会报canvas被污染错误
      let isBase64 = false;
      let canvas = document.getElementById('myCanvas');
      if (isBase64) {
        // base64导出
        let final = canvas.toDataURL();
        console.log('final:', final);
        return;
      }

      // 生成一个Blob 并定义了MIME类型
      const imageBlob = canvas.toBlob((res) => {
        console.log('保存canvas绘制的内容:', res);

        const url = URL.createObjectURL(res);
        const downloadLink = document.createElement('a');
        downloadLink.href = url;
        // 设置文件名和扩展名
        downloadLink.download = 'canvas_image.png';
        downloadLink.click();
        downloadLink.remove();

      }, 'image/png', 1);
    },
  }
}
</script>

<style lang="scss" scoped>

.header {
  margin-bottom: 15px;
}

.content-left {
  // 沿着交叉轴方向自动进行拉升到最大。 让左右高度一致
  //display: flex;
  //align-items: stretch;

  img {
    //max-width: 80px;
    //max-height: 80px;
  }
}

.target-image {
  display: block;
  margin: auto 0;
}

.canvas {
  border:1px solid #ededed;
}



</style>
